Skip to content

Synthesize Langfuse per-branch dispatch spans#190

Merged
chris-colinsky merged 1 commit into
mainfrom
fix/langfuse-parallel-branches-dispatch-span
Jun 25, 2026
Merged

Synthesize Langfuse per-branch dispatch spans#190
chris-colinsky merged 1 commit into
mainfrom
fix/langfuse-parallel-branches-dispatch-span

Conversation

@chris-colinsky

Copy link
Copy Markdown
Member

What

The Langfuse observer now synthesizes a per-branch dispatch-span observation under a parallel_branches dispatcher node, so each branch's inner observations nest under their own branch span (a three-level dispatcher / per-branch-span / inner-nodes tree) instead of parenting directly under the dispatcher.

Why

Observability §4.3 + §8.4.2 (proposal 0042) + proposal 0044 mandate the per-branch dispatch span. The OTel observer produces it (parallel_branches_branch_spans); the Langfuse observer shipped without it (proposal 0044 was OTel-only in v0.11.0), so a parallel-branches Langfuse trace was a flat two-level tree. This closes that gap and un-defers conformance fixture 030.

Changes

  • src/openarmature/observability/langfuse/observer.py: two _InvState caches (parallel_branches_branch_spans, parallel_branches_branch_names); cache the declared branch names on the pb-node started event; synthesize the per-branch observation (once per branch) in _sync_subgraph_observations; open/close helpers; per-branch parent resolution so inner branch nodes reparent under their branch span; and branch_name + fan_out_index on the Generation metadata. Renamed _find_fan_out_node_observation to _find_node_observation (it serves both the fan-out and pb node).
  • tests/conformance/test_observability_langfuse.py: un-defer 030.
  • tests/conformance/test_observability.py: move 030 to _LANGFUSE_HARNESS_FIXTURES.
  • tests/unit/test_observability_langfuse.py: a two-node subgraph-branch test asserting exactly one per-branch observation (guards the synthesis idempotency).
  • CHANGELOG.md: Unreleased/Fixed entry (rides into v0.16.0).

Callable branches (proposal 0075) are unchanged: the single branch observation already is the per-branch Span observation (the branch is the single emitting unit, no inner nodes to nest), so the synthesis is gated to the subgraph-branch path and the existing callable-branch test stays green.

Test plan

  • tests/conformance/test_observability_langfuse.py (030 + the callable-branch test) + tests/unit/test_observability_langfuse.py pass.
  • Full tests/ -> 1464 passed. ruff + pyright clean.

The Langfuse observer did not synthesize the per-branch dispatch-span
observation that observability 4.3 / 8.4.2 + proposal 0044 mandate, so a
parallel_branches trace was two-level (dispatcher -> inner) instead of
three (dispatcher -> per-branch span -> inner). The OTel observer has
this; the Langfuse side shipped without it (0044 was OTel-only in
v0.11.0).

Port the synthesis: cache the declared branch_names per pb node, open a
per-branch Span observation (once per branch) from the first inner branch
event, reparent the inner observations under it, and close on the pb
node's completed event. The per-branch observation carries the OA-emitted
branch_name plus the caller baseline and any per-branch augmentation; the
Generation observation now carries branch_name (and fan_out_index) too.

Un-defer conformance fixture 030.
Copilot AI review requested due to automatic review settings June 25, 2026 01:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Langfuse observer’s span hierarchy for parallel_branches to synthesize a per-branch dispatch-span, aligning the Langfuse trace shape with the spec requirements and the existing OTel observer behavior. It also promotes the previously deferred conformance fixture 030 to run under the Langfuse harness and adds a unit test to ensure the per-branch span synthesis is idempotent.

Changes:

  • Add per-branch dispatch-span synthesis for Langfuse parallel_branches so branch inner-node spans nest under a branch span (3-level tree).
  • Enable conformance fixture 030-caller-metadata-parallel-branches-per-branch for Langfuse by moving it into the Langfuse harness fixture set and updating deferral commentary.
  • Add a unit regression test asserting exactly one per-branch observation is created for a subgraph branch with multiple inner nodes.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/openarmature/observability/langfuse/observer.py Adds per-branch dispatch-span caching/synthesis and re-parents inner branch nodes under the branch span; extends Generation metadata with branch_name/fan_out_index.
tests/unit/test_observability_langfuse.py Adds a regression test ensuring per-branch dispatch synthesis is idempotent for subgraph branches.
tests/conformance/test_observability.py Moves fixture 030 into the Langfuse harness fixture set and removes the old unit-only deferral entry.
tests/conformance/test_observability_langfuse.py Un-defers fixture 030 and updates the rationale comment now that Langfuse supports the required span shape.
CHANGELOG.md Adds an Unreleased/Fixed entry documenting the Langfuse per-branch dispatch-span behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/openarmature/observability/langfuse/observer.py
Comment thread src/openarmature/observability/langfuse/observer.py
Comment thread src/openarmature/observability/langfuse/observer.py
Comment thread src/openarmature/observability/langfuse/observer.py
@chris-colinsky chris-colinsky merged commit 5874173 into main Jun 25, 2026
7 checks passed
@chris-colinsky chris-colinsky deleted the fix/langfuse-parallel-branches-dispatch-span branch June 25, 2026 01:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants